/*******************************************************************************
* Copyright (C) 2014 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/
/*============================================================================
 * 
 * Author(s): Maxim Integrated Products
 * Contributors:
 * Date:          
 * Purpose:       
 * Description:
 * 
 *==========================================================================*/
#include <errors.h>
#include <mml_sc.h>
#include <mml_sc_dev.h>
#include <libemv.h>
#include <emv_config.h>
#include <emv_slot.h>


#define FLAGS_T1_DATA_ACK					0x800
#define FLAGS_T1_SEQ_OK						0x400
#define FLAGS_T1_ABORT						0x200
#define FLAGS_T1_CHAINING					0x100

#define FLAGS_T1_MORE_DATA_BIT				0x20
#define FLAGS_T1_SEQ_DATA_BIT				0x40
#define R_BIT								0x10

void emv_t1_handler(int event );

static int emv_t1_check_lrc(unsigned char *data, unsigned int length)
{
	unsigned char lrc;
	register unsigned int	i;
	
	for ( lrc = 0, i = 0;i < length;i++ )
	{
		lrc ^= data[i];
	}
	/* if lrc == 0 then no error (ERR_NO -> 0)
	 * else lrc != 0 then it's an error */
	return (int) lrc;
}

int emv_t1_block(emv_slot_t *slot,
						unsigned char pcb,
						unsigned char *info,
						unsigned int length)
{
    unsigned char			*frame;
	register unsigned int	i;
    unsigned char			lrc;
    emv_buf_t  *tx_buf = (emv_buf_t  *)&slot->tx_buf;
    emv_buf_t  *rx_buf = (emv_buf_t  *)&slot->rx_buf;

    /* Build the tx frame */
    frame = tx_buf->data;
    *frame++ = 0;               /* Set NAD to zero */
    *frame++ = pcb;
    *frame++ = (unsigned char)length;
    lrc = pcb ^ length;
	if ( info )
	{
		for( i = length;i > 0; )
		{
			i--;
			*(frame + i) = info[i];
			lrc ^= info [i];
		}
	}

	*(frame + length) = lrc;
    tx_buf->length = length + 4;
    /* Clear the response frame */
    rx_buf->length = EMV_CONFIG_APDU_MAX_SIZE;
	for( i = EMV_CONFIG_APDU_MAX_SIZE;i > 0; )
	{
		i--;
		*(rx_buf->data + i) = 0;
	}
    /* Transmit to lower layer */
    slot->error = mml_sc_t1_block(tx_buf->data,
    								tx_buf->length,
    								rx_buf->data,
    								&rx_buf->length);
    return slot->error;
}

int emv_t1_check_block(emv_slot_t *slot)
{
    emv_buf_t  *rx_buf = (emv_buf_t *)&slot->rx_buf; 
    
    /* Check TCK */
    if ( emv_t1_check_lrc(rx_buf->data, rx_buf->length) == NO_ERROR )
    {
        /* Check length, at least {NAD,PCB, Len=0, LRC} */
        if ( rx_buf->length >= 4 )                 
        {
            /* Check NAD */
            if ( rx_buf->data[0] )
            {
                slot->error = N_SCS_ERR_INVALID_NAD;
            }
        }
        else
        {
            slot->error = N_SCS_ERR_UNKNOW;
        }
    }
    else
    {
        slot->error = L1_ErrLrc;
    }
 //   lite_printf("ERR 138:  %d", dev->last_err);
    return slot->error; 
} 

static int emv_t1_get_block_type(emv_slot_t *slot)
{
    int						len;
    int						result = T_OTHER;
    unsigned char			pcb;
    unsigned char			*info;
    emv_buf_t				*rx_buf = (emv_buf_t *)&slot->rx_buf; 
    
	slot->error = NO_ERROR;
	slot->flags &= ~FLAGS_T1_SEQ_OK;
    /* Retrieve the received PCB, LEN bytes */
    pcb = rx_buf->data[1];
    len = rx_buf->length - 4;
    info = rx_buf->data + 3;
    if ( ( pcb & 0x80 ) == IBLOCK )
	{
		if ( slot->flags & FLAGS_T1_MORE_DATA_BIT )
		{
			slot->error = N_SCS_ERR_UNKNOW;
		}
		else
		{			
			if ( (len > 254) || (len == 0) )
			{
				slot->error = N_SCS_ERR_UNKNOW;
			}
			else
			{
				if ( slot->seq != (pcb >> 6) )
				{
					slot->error = L1_ErrSequence;
				}
				else
				{
					slot->flags |= FLAGS_T1_SEQ_OK;
				}
			}
		}

		result = T_IBLOC;
		goto emv_t1_get_block_type_out;
	}

	if ( ( pcb & 0xC0 ) == SBLOCK )
	{
        switch( pcb )
		{
            case ABORT_REQ :
		    case RESYNC_REQ :
			     if ( len )
				    slot->error = N_SCS_ERR_UNKNOW;
			     break;
            case WTX_REQ :
                if ( len != 1 )
				    slot->error = N_SCS_ERR_UNKNOW;
                if ( *info == 0 )
				    slot->error = N_SCS_ERR_UNKNOW;
                break;
            case IFS_REQ :
                if ( len != 1 )
				    slot->error = N_SCS_ERR_UNKNOW;
                else if ( ( *info < 16 ) || ( *info == 255 ) )
				    slot->error = N_SCS_ERR_UNKNOW;
                break;
            default :
                slot->error = N_SCS_ERR_UNKNOW;
		}
		result = T_SBLOC;
		goto emv_t1_get_block_type_out;
	}

	if ( ( pcb & 0xE0 ) == RBLOCK )
	{
		if ( ( ( pcb & 0x0F) > 2) || len )
			/* No valid R-Block */
			slot->error = N_SCS_ERR_UNKNOW;
		else
		{
            if ( ( pcb & R_BIT ) == (( slot->flags & FLAGS_T1_SEQ_DATA_BIT ) >> 2) )
				slot->error = L1_ErrSequence; /* Incorrect reception sequence */
			else
			{
				slot->flags |= FLAGS_T1_SEQ_OK;
				if ( !( slot->flags & FLAGS_T1_MORE_DATA_BIT ) )
				    slot->error = N_SCS_ERR_UNKNOW; /* R Block (i+1) on I(i,0) no chained -> Error */
			}
		}
		result = T_RBLOC;
		goto emv_t1_get_block_type_out;
	}

	slot->error = N_SCS_ERR_UNKNOW;

emv_t1_get_block_type_out:
	return result;
}


int emv_t1_transmit(emv_slot_t  *slot)
{
	int						result;
    unsigned char			pcb;
    emv_apdu_t				*r_apdu = &slot->r_apdu;
    emv_apdu_t				*c_apdu = &slot->c_apdu;
//    emv_buf_t				*tx_buf = &slot->tx_buf;
//    emv_buf_t				*rx_buf = &slot->rx_buf;

    /* Check if an operation is already pending */  
    if ( slot->status & EMV_STATE_OPERATION_PENDING )
    {
        result = COMMON_ERR_ALREADY;
        goto emv_t1_transmit_out;
    }

	slot->retries = 0;
    slot->error = NO_ERROR;
    /* Clear flags */
	slot->flags &= ~(FLAGS_T1_ABORT |
					FLAGS_T1_CHAINING |
					FLAGS_T1_DATA_ACK);
    slot->status |= EMV_STATE_OPERATION_PENDING;
    /* register the handler to manage ATR events */ 
    mml_sc_attach(MML_SC_EVENT_DATA_RECEIVED, (__p_handlers) emv_t1_handler);
    mml_sc_attach(MML_SC_EVENT_ERROR, (__p_handlers) emv_t1_handler);
    slot->state = STATE_T1_START;
    *r_apdu->length = 0;		
	if ( *c_apdu->length > slot->ifsc )
	{
		slot->flags |= FLAGS_T1_MORE_DATA_BIT;
		c_apdu->alloc = slot->ifsc;
	}
	else
	{
		slot->flags &= ~FLAGS_T1_MORE_DATA_BIT;
		c_apdu->alloc = *c_apdu->length;
	}
	/* Send block */
    pcb = (unsigned char)(slot->flags & 0xff);    
	slot->error = emv_t1_block(slot, pcb, c_apdu->data, c_apdu->alloc);
	if ( slot->error  )
	{
		slot->status &= ~EMV_STATE_OPERATION_PENDING;
	}

	result = slot->error;

emv_t1_transmit_out:
	return result;
}

void emv_t1_handler(int event)
{
    unsigned char err = 0;
    unsigned char resp_pcb;
//    unsigned char resp_type;
    unsigned int type = 0;
    mml_sc_slot_info_t slot_info;
    emv_slot_t *slot = emv_get_slot();
    emv_apdu_t *r_apdu = &slot->r_apdu;
    emv_apdu_t *c_apdu = &slot->c_apdu;
    emv_buf_t  *tx_buf = (emv_buf_t *)&slot->tx_buf;
    emv_buf_t  *rx_buf = (emv_buf_t *)&slot->rx_buf; 
    unsigned char *resp_data;
    
    switch( event )
    {
        case MML_SC_EVENT_DATA_SENT:
            break;
        case MML_SC_EVENT_ERROR:
            mml_sc_get_last_error((int *) &slot->error);
            if ( ( slot->error != N_SCS_ERR_PARITY ) &&
				/* Bug correction #749 */
            	( slot->error != N_SCS_ERR_CARD_MUTE ) )
            {
                slot->state = STATE_T1_DONE;
                break;
            }
        case MML_SC_EVENT_DATA_RECEIVED:
            /* Check block response */
            resp_pcb = rx_buf->data[1];
#if 0
            if ( ( resp_pcb & 0xc0 ) == 0x80 )
                resp_type = T_RBLOC;
            else if ( ( resp_pcb & 0xc0 ) == 0xc0 )
                resp_type = T_SBLOC;
            else if ( ( resp_pcb & 0x80 ) == 0 )
                resp_type = T_IBLOC;
            else
                resp_type = T_OTHER;
#endif
            
            resp_data = rx_buf->data + 3;
            /* Reset BWT */
            slot->bwt = 1;
            mml_sc_get_slot_info( &slot_info );
            slot_info.WTX = 1;
            mml_sc_set_slot_info(&slot_info );
   
            if ( slot->error == NO_ERROR )
                slot->error = emv_t1_check_block(slot);
            
            if ( slot->error == NO_ERROR )
                type = emv_t1_get_block_type(slot);

			switch( slot->error )
            {
                case NO_ERROR:
                    if ( ( ( slot->flags & FLAGS_T1_DATA_ACK ) == 0 ) &&
                    	( slot->flags & FLAGS_T1_SEQ_OK ) && *c_apdu->length )
                    {
                        slot->flags |= FLAGS_T1_DATA_ACK;
                        slot->flags ^= FLAGS_T1_SEQ_DATA_BIT;    /* Update emission index */
                        *c_apdu->length -= c_apdu->alloc;
                        c_apdu->data += c_apdu->alloc;
                    }
                            
                    switch( type )
                    {
                        case T_IBLOC :
                            slot->retries = 0;
                            slot->seq ^= 1;  /* I-Bloc valid -> increment reception index & acknowledgement emitted I-Bloc */
                            /* Update receive buffer */
                            emv_memcpy(r_apdu->data + *r_apdu->length, resp_data, rx_buf->length - 4);
                            *r_apdu->length += rx_buf->length - 4;
                                                
                            if ( resp_pcb & FLAGS_T1_MORE_DATA_BIT )
                            {
                            	/* other INF block to wait for ? */
                                slot->state = STATE_T1_SEND_R_BLOCK; /*  I(0,1) or I(1,1) has been received -> We have to send R(N(R)) */
                                slot->flags |= FLAGS_T1_CHAINING;
                            }
                            else
                                slot->state = STATE_T1_DONE; /* INF field with no More Data Bit :-> end of session ( elementary exchange ) */
                            break;
                        case T_RBLOC:
                            if ( slot->flags & FLAGS_T1_ABORT )
                            {
                                *r_apdu->length = 0;
                                slot->error = N_SCS_ERR_CMD_ABORTED;
                                slot->state = STATE_T1_DONE;
                                break;
                            }
                            if( slot->flags & FLAGS_T1_MORE_DATA_BIT )
                            {
                            	/* Test if chaining running */
                            	slot->retries = 0;
                                if ( *c_apdu->length )
                                /* Data to sand lasting */
                                    slot->state = STATE_T1_SEND_I_BLOCK;
                                else
                                    slot->state = STATE_T1_SEND_R_BLOCK;                   
                            }
                            else
                            {
                                if ( *c_apdu->length == 0 )
                                {   /* A R-block can not acknowledge a non-chained block */
                                	slot->state = STATE_T1_SEND_R_BLOCK;
                                    err = BIT_OTHER_ERROR;
                                    if ( ( slot->flags & FLAGS_T1_SEQ_OK ) == 0 )
                                        slot->retries++;
                                }
                                else
                                {
                                    slot->state = STATE_T1_SEND_I_BLOCK; /* I-Bloc to emit again */
                                    slot->retries++;
                                }
                            }
                            break;
                        case T_SBLOC:
                            slot->retries = 0;
                            switch( resp_pcb )
                            {
                                case ABORT_REQ :
                                    if ( slot->mode & EMV_MODE_T1_MANAGE_ABORT )
                                    {
                                        /* Test if chaining */
                                        if ( ( slot->flags & FLAGS_T1_MORE_DATA_BIT ) ||
											( slot->flags & FLAGS_T1_CHAINING ) )
                                        {
                                            slot->flags |= FLAGS_T1_ABORT;
                                            slot->error = emv_t1_block(slot, 
                                                                          ABORT_RSP,
                                                                          0, 0);
                                             *c_apdu->length = 0;
                                             return;
                                        }
                                    }
                                    slot->error = N_SCS_ERR_UNKNOW;
                                    slot->state = STATE_T1_DONE;
                                    break;
                                case RESYNC_REQ :
                                    slot->error = N_SCS_ERR_CMD_ABORTED;
                                    slot->state = STATE_T1_DONE;
                                    break;
                                case WTX_REQ :
                                    slot->bwt = *resp_data;    /* Updating block receive time-out */
                                    mml_sc_get_slot_info( &slot_info);
                                    slot_info.WTX = *resp_data;
                                    mml_sc_set_slot_info( &slot_info);
                                    slot->error = emv_t1_block(slot, WTX_RSP,
                                                        resp_data, 1);
                                    return;
                                case IFS_REQ :
                                    slot->ifsc = *resp_data;
                                    slot->error = emv_t1_block(slot, IFS_RSP,
                                                                resp_data, 1);
                                    return;
                            }
                            break;
                    }
                    break;
                case N_SCS_ERR_CARD_ABSENT:
                    slot->state = STATE_T1_DONE;
                    break;
                case N_SCS_ERR_CARD_MUTE:
                    if ( slot->mode & EMV_MODE_T1_MANAGE_TIMEOUT )
                    {
                        slot->retries++;
                        err = BIT_OTHER_ERROR;
                        slot->state = STATE_T1_SEND_R_BLOCK;
                    }
                    else
                    {
                        slot->state = STATE_T1_DONE;
						/* Bug correction #749 */
                        // HA debug EMV goto out;
                    }
                    break;
                case N_SCS_ERR_INVALID_NAD:
                    slot->retries++;
                    err = BIT_OTHER_ERROR;
                    slot->state = STATE_T1_SEND_R_BLOCK;
                    break;
                case L1_ErrLrc:
                case N_SCS_ERR_PARITY:
                    slot->retries++;
                    err = BIT_PARITE_OU_EDC;
                    slot->state = STATE_T1_SEND_R_BLOCK;
                    break;
                default:
                    if ( ( slot->flags & FLAGS_T1_DATA_ACK )
                        || ((slot->flags & FLAGS_T1_SEQ_OK ) == 0 ) )
                    {
                        /* No error incrementation if block acknowledgement */
                        slot->retries++;
                    }
                    if ( *c_apdu->length &&
                    	( slot->error ==  L1_ErrSequence ) &&
                    	( type == T_RBLOC ) )
                        slot->state = STATE_T1_SEND_I_BLOCK; /* I-Bloc to emit again */
                    else
                    {
                        err = BIT_OTHER_ERROR;
                        slot->state = STATE_T1_SEND_R_BLOCK;
                    }
            }
                
            if ( slot->retries > CONFIG_EMV_T1_MAX_RETRIES )
            {
            	
                if ( slot->mode & EMV_MODE_T1_MANAGE_RESYNC )
                {
                    slot->retries = 0;
                    slot->state = STATE_T1_SEND_RESYNC;
                }
                else
                {
                    slot->error = N_SCS_ERR_UNKNOW;
                    slot->state = STATE_T1_DONE; /* exit! */
                }
            }
                    
            switch( slot->state )
            {         
                case STATE_T1_SEND_I_BLOCK :
                    {
                        unsigned char pcb;
                        unsigned char *data;
                        
                        if ( *c_apdu->length <= 0 )
                        {
                            slot->state = STATE_T1_DONE;
                            break;
                        }
                        
                        data = c_apdu->data;
                        if ( *c_apdu->length > slot->ifsc )
                        {
                            slot->flags |= FLAGS_T1_MORE_DATA_BIT;
                            c_apdu->alloc = slot->ifsc;
                        }
                        else
                        {
                            slot->flags &= ~FLAGS_T1_MORE_DATA_BIT;
                            c_apdu->alloc = *c_apdu->length;
                        }
                        /* Clear data acknowlegde flag */ 
                        slot->flags &= ~FLAGS_T1_DATA_ACK;
                        pcb = (unsigned char)(slot->flags & 0xFF);
                        slot->error = emv_t1_block(slot, pcb, data, c_apdu->alloc);
						slot->state = STATE_T1_RECEIVE_BLOCK;
                    }
                    break;
                case STATE_T1_SEND_R_BLOCK :
                    {
                        unsigned char pcb = RBLOCK;  
                        unsigned char prev_pcb;   
                        
                        if ( slot->seq )
                         /* Index Management PCB.b5 = 1 */
                            pcb |= R_BIT;
                        else
                            pcb &= ~R_BIT;
                        
                        if ( err )
                        {
                            prev_pcb = tx_buf->data[1];
                            if ( ( prev_pcb & 0xc0 ) != 0x80 ) 
                            {
                                pcb &= 0xF0; /* RAZ quartet error */
                                pcb |= err; /* Init quartet error */
                            }
                            else 
                                pcb = prev_pcb;
                        }
                        else
                            pcb &= 0xF0; /* RAZ quartet slot->error */
                        
                        err = 0;
                        slot->error = emv_t1_block(slot, pcb, 0, 0);
                        slot->state = STATE_T1_RECEIVE_BLOCK;
                    }
                    break;
                case STATE_T1_SEND_RESYNC :
					slot->error = emv_t1_block(slot, RESYNC_REQ, 0, 0);
					slot->state = STATE_T1_RECEIVE_RESYNC;
                    break;                
        }   
    }

/* Bug correction #749 */
//out:
    if ( slot->state == STATE_T1_DONE )
    {
        slot->status &= ~EMV_STATE_OPERATION_PENDING;
        if ( slot->error )
        {
        	/* Bug correction #749 */
        	if ( slot->error == N_SCS_ERR_CARD_MUTE )
        	{
        		emv_power_down();
        	}

            emv_call_handler(slot, EMV_EVENT_ERROR);
        }
        else
        {
            emv_call_handler(slot, EMV_EVENT_DATA_RECEIVED);
        }
    }

	return;
}
